bind「要不我們都用資料綁定如何?」Svelte 如此建議。他從那張又熱又厚的綠色皮革椅子當中起身。「我剛好把它停在樹陰下。」
~節錄自《The Great Svelte:第七章》
bind 語法<input> 元素資料綁定 經過了這麼多天的介紹跟實作,相信我們對於 Svelte 當中資料的傳遞應該很熟悉了。就像在第 10 天:Svelte 元件的 Property討論過的,資料可以藉由元件 Property 的方式,從父元件 (parent component) 傳遞給子元件。同時,若是子元件 (child component) 需要改變由父元件傳遞過來的資料,則可以透過第 12 天:Svelte 的事件:事件傳遞介紹的方法,利用事件提醒父元件 (parent) 該改變資料內容了,再由父元件 (parent component) 直接修改資料。
也就是說,在 Svelte 當中,父元件 (parent component) 跟子元件 (child component) 的溝通,是藉由 Property 將資料往下傳遞,並透過往上傳遞的事件來改變父元件 (parent) 當中的資料,如圖一。

圖一、資料向下,事件向上
因此在這樣的設計方針上,就會看到父元件中的資料以 Property 的方式 property={data} 傳遞給子元件,並且有一個事件處理器 on:changeData 接受來自子元件 (child component) 的提醒去做資料更新的動作:
<!-- 在父元件當中 -->
<ChildComponent property={data} on:changeData />
而子元件當中則會看到相對應的該傳向父元件的事件產生器 dispatch('changeData', updatedProperty):
<!-- 在子元件當中 -->
<script>
export let property
const handleChange = () => dispatch('changeData', updatedProperty);
</script>
這樣寫是沒有什麼問題,唯一的問題就是…太麻煩了。對一個有點規模的專案,資料傳遞是這麼的重要,而且發生頻率又是非常頻繁,如果對每個需要傳遞的資料都來上這麼套,撰寫程式碼想必會變得非常辛苦。因此 Svelte 提供了一個方便的語法來處理這個工作,也就是資料綁定 bind。
bind 語法 資料綁定 bind 的任務很簡單,就是要打破資料只能由上而下單一方向改變這件事。藉由 bind,我們可以將父元件 (component) 當中的資料跟子元件 (child component) 的資料綁定在一起,不僅是父元件 (parent component) 的資料改變會影響子元件,還可以在子元件 (child component) 當中直接改變資料,同時將這個變化連動回到父元件 (parent component)。
<!-- 在父元件當中改用 bind -->
<ChildComponent bind:property={data} />
<ChildComponent bind:property={data} />bind:property={data} 就能夠將父元件 (parent component) 的資料跟子元件 (child component) 的資料綁在一起了。data 是父元件 (parent component) 當中的資料,property 則是子元件 (child component) 的 Property,可以作為子元件 (child component) 自己的資料來使用。<!-- 在子元件當中改用 bind -->
<script>
export let property
const handleChange = () => property = updatedProperty;
</script>
const handleChange = () => property = updatedProperty;property 重新賦值,就能直接改變父元件 (parent component) 當中透過 bind:property={data} 綁定在一起的 data 了。是不是很方便呢?就讓我們實際來試試看吧。
還記得我們在色票產生器的專案當中,將所有色票的資料放在主要元件 App.svelte 的變數 palettes 當中,並作為 Property 傳向子元件 Palettes.svelte:
/src/App.svelte
<!-- 省略一些無關的 HTML 元素 -->
<Palettes {palettes} on:changeLock={handleLock} />
同時在子元件 Palettes.svelte 當中,我們接收來自主要元件 App.svelte 的 palettes 這個變數,並透過 dispatch("changeLock", id) 來讓主要元件 App.svelte 知道 palettes 該做出相對應的更新:
<script>
/* 省略一些無關的程式碼 */
import { createEventDispatcher } from "svelte";
export let palettes;
const dispatch = createEventDispatcher();
const handleClick = (id) => dispatch("changeLock", id);
</script>
<!-- 省略一些無關的 HTML 元素 -->
<div class="lock-icon" on:click={() => handleClick(id)}>
{#if locked}
<img src={lock} alt="color-locked" />
{:else}
<img src={unlock} alt="color-unlocked" />
{/if}
</div>
然後再回到主要元件 App.svelte 實作出接收 changeLock 事件的事件處理器 handleLock:
/src/App.svelte
<script>
/* 省略一些無關的程式碼 */
const handleLock = (e) => {
console.log(e);
const targetId = e.detail;
palettes = palettes.map((palette) => {
if (palette.id === targetId) {
return { ...palette, locked: !palette.locked };
} else {
return palette;
}
});
};
</script>
經過一番折騰,我們的解鎖/上鎖功能終於做出來了。現在既然學會了 bind ,就讓我們看看如何省去這些折騰吧:
/src/App.svelte
<!-- 省略一些無關的 HTML 元素 -->
<Palettes bind:palettes={palettes} />
<Palettes bind:palettes={palettes} />App.svelte 當中,用 bind:palettes={palettes} 將主要元件的變數 palettes 與 Palettes.svelte 這個元件的 Property palettes 綁定在一起。還記的我們在第 10 天:Svelte 元件的 Property 當中提過,只要變數跟 Property 名稱相同,就可以簡寫。在使用 bind 的時候也不例外。所以我們現在這行程式碼,其實也可以寫成:<Palettes bind:palettes />
就是這麼簡單。那讓我們進到子元件 Palettes.svelte,來看看該做哪些修改:
/src/lib/Palettes.svelte
<script>
/* 省略一些無關的程式碼 */
// import { createEventDispatcher } from "svelte";
export let palettes;
// const dispatch = createEventDispatcher();
// const handleClick = (id) => dispatch("changeLock", id);
</script>
<!-- 省略一些無關的 HTML 元素 -->
<div class="lock-icon" on:click={() => (locked = !locked)}>
{#if locked}
<img src={lock} alt="color-locked" />
{:else}
<img src={unlock} alt="color-unlocked" />
{/if}
</div>
第三行:// import { createEventDispatcher } from "svelte";
使用了 bind,我們就不用向主要元件發送事件提醒修改資料了,所以 createEventDispatcher 就用不到了。
第六行:// const dispatch = createEventDispatcher();
這個用不到了。
第七行:// const handleClick = (id) => dispatch("changeLock", id);
這個也用不到了。
第十一行:<div class="lock-icon" on:click={() => (locked = !locked)}>
因為 bind 已經將主要元件跟子元件的 palettes 綁定在一起,現在我們直接在子元件當中修改資料,這個改動就會連動到主要元件。所以簡簡單單的說 on:click={() => (locked = !locked)} 就可以囉!
回到主要元件 App.svelte,有個事件處理器 handleLock 再也用不到了:
/src/App.svelte
<script>
/* 省略一些無關的程式碼 */
// const handleLock = (e) => {
// console.log(e);
// const targetId = e.detail;
// palettes = palettes.map((palette) => {
// if (palette.id === targetId) {
// return { ...palette, locked: !palette.locked };
// } else {
// return palette;
// }
// });
// };
/* 稍微修改一下隨機產生色票的程式碼 */
onMount(() => {
const intervalId = setInterval(() => {
palettes = palettes.map((palette) => {
if (palette.locked) return palette;
else
return {
...palette,
hex: generateHex(),
};
});
}, 2000);
return () => clearInterval(intervalId);
});
</script>
第三行:// const handleLock = (e) => {
辛辛苦苦作出來的 handleLock 可以功成身退了。
第十八行:palettes = palettes.map((palette) => {
跟 bind 主題無關,稍微修改一下第 21 天:Svelte 的生命週期函式:onMount做出來,隨機產生色票的程式碼。
第十九行:if (palette.locked) return palette;
如果 palette.locked,也就是色票處於上鎖的狀態,那就不要改變這張色票的顏色。否則的話,就隨機產生新的顏色。

圖二、輕輕鬆鬆讓色票解鎖/上鎖
學會 bind 之後,是不是覺得 Svelte 程式碼越看越親切了呢!那麼今天的內容就到這裡了,謝謝大家。